/**
 * Workload Generator
 */
#include <stdio.h>
#include <math.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include "cJSON.h"
#include "generator.h"
#include "random.h"

cJSON * parse_config() {
  FILE *f= fopen("config","rb");
  fseek(f,0,SEEK_END);
  long len=ftell(f);
  fseek(f,0,SEEK_SET);
  char *data=malloc(len+1);
  fread(data,1,len,f);
  fclose(f);
  cJSON *config = cJSON_Parse(data);
  free(data);
  return config;
}

int workload_generator(cJSON *config, char *resource, resource_model *(*init)(cJSON *), pthread_t *thread)
{
  cJSON *p;
  for(p = config->child; p != NULL; p = p->next)
    {
      //Take a look
      if(strcmp(resource, p->child->valuestring) == 0)
	{
	  break;
	}
    }
  if(NULL == p) 
    {
      return 0;
    }
  resource_model *model = init(p);
  pthread_create(thread, NULL, thr_resource, model);
}

/**
 * Acts as the controller for each resource thread
 */
void * thr_resource(void *config)
{ 
  struct timespec req={0};
  setup_rng_buffer( pthread_self() );
  struct timeval current_time = {0}; 
  //Initialize
  schedule *sched;
  resource_model *model = (resource_model *)config;
  gettimeofday(&current_time,NULL);
  schedule *to_add = model->birth(model->state);
  sched = to_add;
  
  while(has_event(sched)) {
    event *e = next_event(sched);
    unsigned int time_to_sleep = timeval_diff(&current_time, e->timestamp);
    usleep(time_to_sleep);
    gettimeofday(&current_time,NULL);
    schedule *s = e->job(e->args);
    if(s != NULL) {
      if(s->head != NULL)
        {
          sched = join_schedules(sched, s);
        }
    }
    free(e);
  }
}

/**
 * Gets the point in time that is ms milliseconds from now
 */
struct timeval *ms_from_now(double ms){
  struct timeval *t = malloc(sizeof(struct timeval));
  gettimeofday(t,NULL);
  unsigned int seconds = ms / 1000;
  unsigned int microseconds = (ms - seconds) * 1000;
  unsigned int total_microseconds = (t->tv_usec + microseconds) % 1000000;
  unsigned int seconds_to_add = (t->tv_usec + microseconds) / 1000000;
  t->tv_sec += seconds + seconds_to_add;
  t->tv_usec = total_microseconds;
  return t;
}

/**
 * Get the difference in milliseconds between 2 times
 */
unsigned int timeval_diff(struct timeval *starttime, struct timeval *finishtime)
{
  if(finishtime->tv_sec < starttime->tv_sec ||((finishtime->tv_sec == starttime->tv_sec) && (finishtime->tv_usec < starttime->tv_usec))) {
    return 0;
  }
  unsigned int msec;
  msec=(finishtime->tv_sec-starttime->tv_sec)*1000;
  msec+=(finishtime->tv_usec-starttime->tv_usec)/1000;
  return msec;
}

